package jwt

import (
	"fmt"
	"time"

	"gitee.com/huangxinbo/xcas/internal/config"
	"gitee.com/huangxinbo/xcas/internal/model/db_model"
	"gitee.com/huangxinbo/xcas/pkg"
	"gitee.com/huangxinbo/xcas/pkg/gin_extend"
	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt/v5"
	"github.com/google/uuid"
)

// CustomClaims 自定义 JWT claims，包含用户扩展信息
type CustomClaims struct {
	jwt.RegisteredClaims
	Name   string `json:"name,omitempty"`
	Email  string `json:"email,omitempty"`
	Image  string `json:"image,omitempty"`
	Role   int16  `json:"role,omitempty"`
	Region string `json:"region,omitempty"`
}

type JWT struct {
	config *config.Config
}

func NewJWT(config *config.Config) *JWT {
	return &JWT{
		config: config,
	}
}

// GenerateToken 生成 JWT 令牌
func (j *JWT) GenerateToken(ctx *gin.Context, user *db_model.User) (string, time.Time, error) {
	// 直接使用配置中的密钥（不需要解码，因为配置中已经是字符串）
	key := []byte(j.config.JWT.Secret)

	// 设置令牌过期时间（1小时）
	expireTime := time.Now().Add(time.Duration(pkg.GetEnvInt("JWT.EXPIRES_AT", 1)) * time.Hour)

	uuid, err := uuid.NewRandom()
	if err != nil {
		return "", time.Time{}, fmt.Errorf("failed to generate UUID: %w", err)
	}

	// 创建自定义 claims
	claims := &CustomClaims{
		RegisteredClaims: jwt.RegisteredClaims{
			Audience:  []string{gin_extend.GetBaseURL(ctx)},
			ExpiresAt: jwt.NewNumericDate(expireTime),
			IssuedAt:  jwt.NewNumericDate(time.Now()),
			NotBefore: jwt.NewNumericDate(time.Now()),
			Issuer:    j.config.JWT.Issuer,
			Subject:   user.ID,
			ID:        uuid.String(),
		},
		Name:   user.Name,
		Email:  user.Email,
		Image:  pkg.GetStringValue(user.Image),
		Role:   user.Role,
		Region: pkg.GetStringValue(user.Region),
	}

	// 创建令牌
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

	// 签名令牌
	tokenString, err := token.SignedString(key)
	if err != nil {
		return "", time.Time{}, fmt.Errorf("failed to sign JWT token: %w", err)
	}

	return tokenString, expireTime, nil
}

// ValidateToken 验证 JWT 令牌
func (j *JWT) ValidateToken(ctx *gin.Context, tokenString string) (*CustomClaims, error) {
	key := []byte(j.config.JWT.Secret)

	// 解析和验证令牌
	token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
		// 验证签名方法
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
		}
		return key, nil
	})

	if err != nil {
		return nil, fmt.Errorf("failed to parse JWT token: %w", err)
	}

	if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
		// 检查 token 是否在黑名单中
		if j.isTokenBlacklisted(ctx, claims.Subject) {
			return nil, fmt.Errorf("JWT token is blacklisted")
		}
		return claims, nil
	}

	return nil, fmt.Errorf("invalid JWT token")
}

// RefreshToken 更新 JWT 令牌的过期时间
func (j *JWT) RefreshToken(tokenString string) (string, time.Time, error) {
	key := []byte(j.config.JWT.Secret)

	// 解析现有令牌但不验证过期时间（因为可能已经过期）
	token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
		// 验证签名方法
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
		}
		return key, nil
	})

	if err != nil {
		return "", time.Time{}, fmt.Errorf("failed to parse JWT token: %w", err)
	}

	// 获取 claims
	claims, ok := token.Claims.(*CustomClaims)
	if !ok {
		return "", time.Time{}, fmt.Errorf("invalid JWT claims")
	}

	// 检查 token 是否在黑名单中
	if j.isTokenBlacklisted(nil, claims.Subject) {
		return "", time.Time{}, fmt.Errorf("JWT token is blacklisted")
	}

	// 计算新的过期时间（1小时）
	newExpireTime := time.Now().Add(time.Duration(pkg.GetEnvInt("JWT.EXPIRES_AT", 1)) * time.Hour)

	// 更新 claims 中的过期时间
	claims.ExpiresAt = jwt.NewNumericDate(newExpireTime)
	claims.IssuedAt = jwt.NewNumericDate(time.Now())
	claims.NotBefore = jwt.NewNumericDate(time.Now())

	// 创建新的令牌
	newToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

	// 签名新的令牌
	newTokenString, err := newToken.SignedString(key)
	if err != nil {
		return "", time.Time{}, fmt.Errorf("failed to sign refreshed JWT token: %w", err)
	}

	return newTokenString, newExpireTime, nil
}

// isTokenBlacklisted 检查 JWT token 是否在黑名单中
func (j *JWT) isTokenBlacklisted(ctx *gin.Context, userID string) bool {
	// 注意：由于 JWT 结构体没有 Redis 客户端，这里需要在实际使用中通过其他方式实现
	// 在实际部署中，可以通过依赖注入或其他方式获取 Redis 客户端
	// 这里返回 false 表示不进行黑名单检查，实际部署时需要实现这个逻辑
	return false
}
